CloudFormation の組み込み関数 Fn::Sub 対 Fn::Join
多くの個人が、CloudFormationテンプレート内の静的テキストや変数などの情報を結合するために、Fn::Joinコマンドを使用しています。これはうまくいくのですが、物事が複雑になるにつれて、混沌として理解しづらくなることがあります。Fn::Subは、テンプレート定義の簡略化を支援します。
まず、Fn::Joinがどのように機能するかを見てみましょう。
Join関数を使うと、テキストと変数を結びつけることができる。構文はJSONやYAMLなど、さまざまな方法で表現することができます。
以下は、Joinのシンタックスの例です。
Fn::JoinをJsonに使います
。
{ "Fn::Join": [ "", [ "arn:aws:s3:::", {"Ref": "ImageBucketName","/*"} ] ] }
Fn::JoinをYAMLに使います
Resource: - Fn::Join: - "" - - "arn:aws:s3:::" - !Ref ImageBucketName - "/*"
また、YAML用のFn::Joinはこのように書くことができます。
!Join [ "", [ "arn:aws:s3::", !Ref ImageBucketName, "/*" ] ]
見ての通り、Fn::JoinのYMAL構文は読みやすさに関しては大きな違いはなく、相変わらず複雑な構文で、何をやっているのか理解するために多少の努力が必要で、私にとっては「Regular Expression」を読んでいるような感覚です。
Fn::Subを使います
Fn::Subを使っても全く同じことができるのですが、よりわかりやすい方法で行っています。Fn::Joinが与えられた値を使って文字列を生成する場合、Fn::Subは与えられた文字列に値を代入します。
では、どういうことなのか、例を挙げて説明しましょう。
Fn:Sub 例
Resource: !Sub "arn:aws:s3:::${ImageBucketName}/*"
簡単でしょう?Fn::Joinを使うのは上で見たのと全く同じですが、こちらはずっとシンプルで読みやすいです。先ほどの例では、${}が入った文字列があり、${}がすることは、その中の変数の値を文字列に置き換えるだけですが、Subにはもっと多くのことがあります。
Subでは、独自のパラメータを用意することができます。例として:
!Sub - 'arn:aws:s3:::${ImageBucketName}/*' - { ImageBucketName: Ref MyBucket }
リソース生成の問題をデバッグする場合、Fn::Subのアプローチはかなり把握しやすく、変数の代入を実行して問題があるかどうかを確認するのがずっと簡単です。
しかし、Serverless FrameworkとFn::Subに関しては、問題があります。
Serverless framework Fn::Sub
. ServerlessでFn::Subを使うには、ServerlessとFn::Subの${}の構文が同じなので、この問題を解決するために、npmからプラグインを導入する必要があります。
npm install serverless-cloudformation-sub-variables
インストールしたら、serverless.ymlファイルのpluginセクションにこのプラグインを追加してください。
plugins: - serverless-cloudformation-sub-variables
このプラグインは基本的にFn::Subの${}を#{}に置き換えることで、Serverlessで問題を起こす${VarName}を使う代わりに、#{VarName}を使えるようにするものです。
まとめ
どちらも同じことをするのであって、一長一短があります、サーバーレスでは「!Sub」が不便で、「Fn::Join」は読みにくくて作業しにくいでも、Fn::Joinの複雑さに比べたら、Fn::Subの不便さは大したことないので、私はFn::Subの方が好きです。